/*
 * MIT License
 *
 * Copyright (c) 2019 Mr.css
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

package cn.seaboot.commons.reflect;

import cn.seaboot.commons.exception.ReflectException;
import com.esotericsoftware.reflectasm.MethodAccess;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 更加高效的 java 反射工具类
 * <p>
 * 出于安全考虑，将所有的字段置为 final 类型，
 * 对于 field/setter/getter 不匹配的对象，提前抛出异常。
 *
 * @author Mr.css
 * @version 2021-02-08 10:37
 */
public class ObjectField {

    /**
     * 字节码工具
     * <p>
     * field 暴力访问效率奇高，method.invoke() 相对低一些，这个工具介于二者之间
     */
    private final MethodAccess access;

    /**
     * 字段名称
     */
    private final String name;

    /**
     * 字段对象实体类
     */
    private final Field field;

    /**
     * 归属的 java 类型
     */
    private final Class<?> classType;

    /**
     * 字段类型
     */
    private final Class<?> type;

    /**
     * setter 函数名
     * <p>
     * getter 与 field 名称可能不匹配，这种情况下，这个字段可能是空的，
     * 进行反射操作会抛出异常，这时候需要手动地把名称填上。
     */
    private final String setter;

    /**
     * getter 函数名
     * <p>
     * getter 与 field 名称可能不匹配，这种情况下，这个字段可能是空的，
     * 进行反射操作会抛出异常，这时候需要手动地把名称填上。
     */
    private final String getter;

    /**
     * 对象类型
     */
    private final ObjectType objectType;

    /**
     * constructor
     *
     * @param access 字节码解析工具
     * @param clazz  所属的 class
     * @param field  归属的 field
     */
    public ObjectField(MethodAccess access, Class<?> clazz, Field field) {
        this.classType = clazz;
        this.access = access;
        this.field = field;
        this.name = field.getName();
        this.type = field.getType();
        this.objectType = ObjectType.of(this.type);

        // getter
        Method getter = MethodUtils.getGetter(clazz, name);
        if (getter == null) {
            // getter and field not match
            throw new ReflectException("getter not found: " + clazz + "." + name);
        } else {
            this.getter = getter.getName();
        }

        // setter
        Method setter = MethodUtils.getSetter(clazz, name, this.type);
        if (setter == null) {
            // setter and field do not match
            throw new ReflectException("setter not found: " + clazz + "." + name);
        } else {
            this.setter = setter.getName();
        }
    }

    /**
     * 返回注解
     *
     * @param annotationClass 注解类型
     * @param <T>             泛型
     * @return 注解
     */
    public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
        return field.getAnnotation(annotationClass);
    }

    /**
     * 返回注解
     *
     * @return 注解
     */
    public Annotation[] getAnnotations() {
        return field.getAnnotations();
    }

    /**
     * 设置值
     *
     * @param obj   目标对象
     * @param value 值
     */
    public void setValue(Object obj, Object value) {
        access.invoke(obj, setter, value);
    }

    /**
     * 获取值
     *
     * @param obj 目标对象
     */
    public Object getValue(Object obj) {
        return access.invoke(obj, getter);
    }

    //getter/setter-------------------------------------------------

    @NotNull
    public MethodAccess getAccess() {
        return access;
    }

    @NotNull
    public String getName() {
        return name;
    }

    /**
     * 字段所属的 class
     *
     * @return class
     */
    @NotNull
    public Class<?> ofClass() {
        return classType;
    }

    @NotNull
    public Class<?> getType() {
        return type;
    }

    @Nullable
    public String getSetter() {
        return setter;
    }

    @Nullable
    public String getGetter() {
        return getter;
    }

    @NotNull
    public Field getField() {
        return field;
    }

    @NotNull
    public ObjectType getObjectType() {
        return objectType;
    }

    @Override
    public String toString() {
        return "ObjectField{" +
                "methodAccess=" + access +
                ", name='" + name + '\'' +
                ", field=" + field +
                ", classType=" + classType +
                ", fieldType=" + type +
                ", setter='" + setter + '\'' +
                ", getter='" + getter + '\'' +
                ", objectType=" + objectType +
                '}';
    }
}
